unit UField;

interface

uses
  SysUtils, Classes, IniFiles, Generics.Collections;

const
  CComma = ', ';
  CDoubleDot = ':';
  CEqualis = ' = ';
  CEq = '%s' + CEqualis + '%s';
  CAND = ' AND ';
  CID = 'ID';
  CTableConfig = '%s.config';
  CColumns = '%s.columns';
  CRefKeys = '%s.reference_keys';
  CRefTables = '%s.reference_tables';
  CRefFields = '%s.reference_fields';
  CRefResults = '%s.reference_outputs';
  CPrefSort = '%s.preferable_sort_fields';
  CShortNames = 'short_names';
  CFieldName = 'a%d.%s';
  CMainFieldName = 'a0.%s';
  CFieldRow = CFieldName + ' as "%s"'; {a%d.%s as "%s"}
  CJoinFmt = ' INNER JOIN %s a%d on a%1:d.%s = a0.%s';
  CSelectAll = 'SELECT * FROM %s';

type
  TPObjectDynArray = array of PObject;

  PSQLRec = ^TSQLRec;
  
  TSQLRec = record     
    SelectStr, FromStr, WhereStr, SortStr: string;
    InsertStr, ModifyStr, DeleteStr: string;
    function Select: string;
    function SelectFrom: string;
  end;

  TSelfCheckingClass = class(TObject)
  private
    function IsOK: boolean; virtual; abstract;
  public
    property OK: boolean read IsOK;
  end;

  TRefTableDef = class(TSelfCheckingClass)
  private
    function IsOK: boolean; override;
  public
    Key, RefTable, RefKey: string;
    Index: integer;
    DataRef: TObject;
    function GetJoin: string;
    constructor Create(AKey, ARefTable, ARefKey: string); overload;
  end;

  TRefTableDefObjectList = TObjectList<TRefTableDef>;

  TSchField = class(TSelfCheckingClass)
  private
    FPreferableSortField: string;
    function IsOK: boolean; override;
    function GetEditField: string; virtual;
    function GetSortField: string;
    function GetFieldPath: string;
  public
    Name, Alias, Caption, Short: string;
    Order: integer;
    function GetQueryField: string;
    property FieldPath: string read GetFieldPath;
    property EditField: string read GetEditField;
    property SortField: string read GetSortField write FPreferableSortField;
    constructor Create(AName: string); overload; virtual;
  end;

  TSchLookupField = class(TSchField)  
  private
    function IsOK: boolean; override;
    function GetEditField: string; override;
  public  
    TableDef: TRefTableDef;
    constructor Create(AName: string); override;
  end;

  TSchFieldClass = class of TSchField;

  TSchLookupFields = TList<TSchLookupField>;

function GetFieldClass(lookup: boolean): TSchFieldClass;
function TableDefsEqual(a, b: TRefTableDef): boolean;
function Conjuct(S: TStrings; const Delim: string): string;

implementation

function GetFieldClass(lookup: boolean): TSchFieldClass;
begin
  if lookup then
    Result := TSchLookupField
  else
    Result := TSchField;
end;

function TableDefsEqual(a, b: TRefTableDef): boolean;
begin
  Result :=
    SameText(a.Key, b.Key) and
    SameText(a.RefTable, b.RefTable) and
    SameText(a.RefKey, b.RefKey);
end;

function Conjuct(S: TStrings; const Delim: string): string;
var
  i: integer;
begin
  if S.Count = 0 then
    Exit('');
  Result := S[0];
  for i := 1 to S.Count - 1 do
    Result := Result + Delim + S[i];
end;

{ TSchField }

constructor TSchField.Create(AName: string);
begin
  inherited Create;
  Name := AName;
  Alias := AName;
end;

function TSchField.GetEditField: string;
begin
  Result := Alias;
end;

function TSchField.GetFieldPath: string;
begin
  Result := Format(CFieldName, [Order, Name]);
end;

function TSchField.GetQueryField: string;
begin
  Result := Format(CFieldRow, [Order, Name, Alias]);
end;

function TSchField.GetSortField: string;
begin
  if FPreferableSortField = '' then
    Result := Name
  else
    Result := FPreferableSortField;
  Result := Format(CFieldName, [Order, Result])
end;

function TSchField.IsOK: boolean;
begin
  Result := (Name <> '') and (Alias <> '') and (Order >= 0)
end;

{ TSchLookupField }

constructor TSchLookupField.Create(AName: string);
begin
  inherited Create;
  Alias := AName;
end;

{ TTableDef }

constructor TRefTableDef.Create(AKey, ARefTable, ARefKey: string);
begin
  Key := AKey;
  RefTable := ARefTable;
  RefKey := ARefKey;
end;

function TRefTableDef.GetJoin: string;
begin         
  Result := Format(CJoinFmt, [RefTable, Index, RefKey, Key]);
end;

function TRefTableDef.IsOK: boolean;
begin
  Result := (Key <> '') and (RefTable <> '') and (RefKey <> '') and (Index > 0);
end;

function TSchLookupField.GetEditField: string;
begin
  Result := TableDef.Key;
end;

function TSchLookupField.IsOK: boolean;
begin
  Result := inherited IsOK and (TableDef <> nil) and TableDef.IsOK;
end;

{ TSQLRec }

function TSQLRec.Select: string;
begin
  Result := Format('%s %s %s %s', [SelectStr, FromStr, WhereStr, SortStr]);
end;

function TSQLRec.SelectFrom: string;
begin
  Result := SelectStr + ' ' + FromStr
end;

end.
